package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.apis.schedule.IScheduleClient;
import com.heima.common.constants.WemediaConstants;
import com.heima.common.constants.WmNewsMessageConstants;
import com.heima.common.exception.CustomException;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.enums.TaskTypeEnum;
import com.heima.model.mess.DownOrUpMess;
import com.heima.model.schedule.dtos.Task;
import com.heima.model.wemedia.dtos.WmNewsDto;
import com.heima.model.wemedia.dtos.WmNewsPageReqDto;
import com.heima.model.wemedia.pojos.*;
import com.heima.model.wemedia.vo.WmNewsContentVO;
import com.heima.utils.common.ProtostuffUtil;
import com.heima.utils.thread.WmThreadLocalUtil;
import com.heima.wemedia.mapper.WmChannelMapper;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmChannelService;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class WmNewsServiceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;
    @Autowired
    private WmMaterialMapper wmMaterialMapper;
    @Autowired
    private WmNewsAutoScanService wmNewsAutoScanService;
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;
    @Autowired
    private IScheduleClient iScheduleClient;

    @Override
    public ResponseResult findNews(WmNewsPageReqDto dto) {

        //判断登录状态
        WmUser user = WmThreadLocalUtil.getUser();
        if (user == null) {
            //用户未登录
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }

        Page<WmNews> page = new Page<>();
        page.setCurrent(dto.getPage());
        page.setSize(dto.getSize());

        LambdaQueryWrapper<WmNews> wrapper = Wrappers.<WmNews>lambdaQuery().eq(WmNews::getUserId, user.getId())
                .orderByDesc(WmNews::getPublishTime);

        //校验文章状态
        if (dto.getStatus() != null) {
            wrapper.eq(WmNews::getStatus, dto.getStatus());
        }

        //关键字
        if (StringUtils.isNotBlank(dto.getKeyword())) {
            wrapper.like(WmNews::getTitle, dto.getKeyword());
        }

        //频道列表
        if (dto.getChannelId() != null) {
            wrapper.eq(WmNews::getChannelId, dto.getChannelId());
        }

        //发布时间 between #{start} and #{end}
        if (dto.getBeginPubDate() != null && dto.getEndPubDate() != null) {
            wrapper.between(WmNews::getPublishTime, dto.getBeginPubDate(), dto.getEndPubDate());

        }

        Page<WmNews> pageResult = page(page, wrapper);
        PageResponseResult result = new PageResponseResult((int) pageResult.getCurrent(), (int) pageResult.getSize()
                , (int) pageResult.getTotal());
        result.setData(pageResult.getRecords());

        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submit(WmNewsDto dto) {

        //判断登录状态 网关拿到token解析userId -> 微服务 拦截器将userId -> ThreadLocal
        WmUser user = WmThreadLocalUtil.getUser();
        if (user == null) {
            //用户未登录
            throw new CustomException(AppHttpCodeEnum.NEED_LOGIN);
        }

        //1.接收参数
        WmNews wmNews = new WmNews();
        //属性名一致 类型一致 封面images
        BeanUtils.copyProperties(dto, wmNews);
        //提取内容中的图片
        List<String> contentImageUrls = getContentImageUrls(dto);

        //处理自动选择封面
        if (Objects.equals(dto.getType(), WemediaConstants.WM_NEWS_TYPE_AUTO)) {
            int size = contentImageUrls.size();
            if (size >= 3) {
                //3.图片数量>=3 多图 选择前三张
                dto.setImages(contentImageUrls.subList(0, 3));
                wmNews.setType(WemediaConstants.WM_NEWS_MANY_IMAGE);
            } else if (size > 0) {
                //2.图片数量1-2 单图 选择第一张图片
                dto.setImages(contentImageUrls.subList(0, 1));
                wmNews.setType(WemediaConstants.WM_NEWS_SINGLE_IMAGE);
            } else {
                //1.图片数量0 无图
                dto.setImages(null);
                wmNews.setType(WemediaConstants.WM_NEWS_NONE_IMAGE);
            }

        }

        //封面字段无法拷贝，需要手动进行处理
        //[a.jpg,b.jpg,c.jpg] ->  a.jpg,b.jpg,c.jpg
        if (!CollectionUtils.isEmpty(dto.getImages())) {
            wmNews.setImages(StringUtils.join(dto.getImages(), ","));
        }

        //2.文章保存逻辑
        wmNews.setUserId(user.getId());
        //判断是否是提交
        if(Objects.equals(dto.getStatus(),WmNews.Status.SUBMIT.getCode())){
            wmNews.setSubmitedTime(new Date());
        }

        if (wmNews.getId() != null) {

            //删除文章和图片关联关系 delete from xxx where news_id = 1
            wmNewsMaterialMapper.delete(Wrappers.<WmNewsMaterial>lambdaQuery()
                    .eq(WmNewsMaterial::getNewsId, wmNews.getId())
            );

            //修改
            updateById(wmNews);
        } else {
            wmNews.setCreatedTime(new Date());
            wmNews.setEnable((short)1);
            //新增
            save(wmNews);
        }

        //3.维护图片和文章关联关系
        if(Objects.equals(dto.getStatus(),WmNews.Status.NORMAL.getCode())){
            //如果是草稿直接结束
            return;
        }

        //保存内容中图片和文章关联关系 a.jpg b.jpg
        saveMaterialAndNews(wmNews.getId(), contentImageUrls,WemediaConstants.WM_CONTENT_REFERENCE);


        //保存封面中图片和文章关联关系
        saveMaterialAndNews(wmNews.getId(), dto.getImages(),WemediaConstants.WM_COVER_REFERENCE);

        System.out.println(Thread.currentThread().getName());
        //执行审核逻辑
        //wmNewsAutoScanService.autoScan(wmNews);

        //通过任务框架提交任务
        Task task = new Task(TaskTypeEnum.NEWS_SCAN_TIME);
        task.setExecuteTime(wmNews.getPublishTime().getTime());
        byte[] serialize = ProtostuffUtil.serialize(wmNews); //使用protostuff替代json为了提升序列化性能并且数据量比较小
        task.setParameters(serialize);
        iScheduleClient.addTask(task);
    }


    @Scheduled(fixedRate = 1000L)
    public void handleAutoScan(){
        ResponseResult poll = iScheduleClient.poll(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType(), TaskTypeEnum.NEWS_SCAN_TIME.getPriority());
        if(poll != null && Objects.equals(poll.getCode(),AppHttpCodeEnum.SUCCESS.getCode()) && poll.getData() != null){
            String s = JSON.toJSONString(poll.getData());
            Task task = JSON.parseObject(s, Task.class);
            //获取任务对象中参数属性-文章数据
            byte[] parameters = task.getParameters();
            WmNews wmNews = ProtostuffUtil.deserialize(parameters, WmNews.class);
            wmNewsAutoScanService.autoScan(wmNews);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void downOrUp(WmNewsDto dto) {
        //1.根据id查询文章
        WmNews wmNews = getById(dto.getId());
        //2.判断文章是否存在
        if(wmNews == null){
            //文章不存在
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //3.判断文章是否发布
        //校验enable值是否有效
        if(!Objects.equals(wmNews.getStatus(),WmNews.Status.PUBLISHED.getCode())
            || Objects.equals(wmNews.getEnable(),dto.getEnable())
        ){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }

        //4.修改文章enable
//        wmNews.setEnable(dto.getEnable());
        //updateById(wmNews);
        //对文章用户进行校验 数据权限校验
        boolean update = update(Wrappers.<WmNews>lambdaUpdate()
                .eq(WmNews::getId, dto.getId())
                .eq(WmNews::getUserId, WmThreadLocalUtil.getUser().getId())
                .set(WmNews::getEnable, dto.getEnable())
        );
        if(!update){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        //5.发送消息
        DownOrUpMess downOrUpMess = new DownOrUpMess();
        downOrUpMess.setArticleId(wmNews.getArticleId());
        downOrUpMess.setEnable(dto.getEnable());
        kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(downOrUpMess));

    }

    //保存素材和文章关联关系
    private void saveMaterialAndNews(Integer newsId, List<String> urls,Short type) {
        if(!CollectionUtils.isEmpty(urls)){
            //3.1 根据图片url查询素材id select id from xxx where url in ()
            List<WmMaterial> wmMaterials = wmMaterialMapper
                    .selectList(Wrappers.<WmMaterial>lambdaQuery().select(WmMaterial::getId)
                            .in(WmMaterial::getUrl, urls));

            List<Integer> ids = wmMaterials.stream().map(WmMaterial::getId).collect(Collectors.toList());

            //判断id集合和图片集合数量是否一致
            if(ids.size() != urls.size()){
                //有部分图片被删除
                log.error("图片数据被删除!");
                throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
            }

            //批量写入
            wmNewsMaterialMapper.insertBatch(ids,newsId,type);
        }
    }

    /**
     * 获取参数中所有的图片集合
     *
     * @param dto
     */
    private List<String> getContentImageUrls(WmNewsDto dto) {

        //提取内容中的图片
        List<WmNewsContentVO> contentVos = JSON.parseArray(dto.getContent(), WmNewsContentVO.class);

        //1.筛选出图片 2.提取图片url 3.去重 4.转换成list集合
        return contentVos.stream().filter(data -> Objects.equals(data.getType(), WemediaConstants.WM_NEWS_TYPE_IMAGE))
                .map(WmNewsContentVO::getValue).distinct().collect(Collectors.toList());
    }

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

    public static void main(String[] args) {
        HashSet<Object> objects = new HashSet<>();
        objects.add("2");
        objects.add("1");
        objects.add("3");
        objects.add("4");
        System.out.println(objects);

        for (Object object : objects) {
            System.out.println(hash(object));
        }
    }
}